/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */
#pragma once

#include <math/Rect.h>
#include "Node2D.h"
#include <animate/Soul.h>
#include "Factory.h"
#include <lib3d/anim2d/AnimatedSequences.h>
#include <HardwareDependentObject.h>
#include <lib3d/hard/Manager.h>
#include <Object2D.h>
#include <CompilerFeatures.h>

namespace liba {
namespace lib3d {
namespace node {

class Animation2D : public Node2D,
	public HardwareDependentObject<hard::Manager, hard::VertexBuffer>
{
	NODE_DEFINITION(Animation2D)
public:
	Animation2D( const Atom & name, Node * parent );
	Animation2D(const Animation2D& rhs);
	virtual ~Animation2D();

	//designed for positive and negative time cycles
	virtual void LifeCycle(time_float delta_time);

	virtual void RenderToList( hard::Hardware * hardware, hard::RenderList * render_list ) LIBA_OVERRIDE;

	//will remove itself from animate::Soul, you have to call life_cycle manually
	void set_manual_time_control(bool manual);

	void set_time(double time);

	//returns double. frame_number += delta_time / frame_time;
	double get_frame_number()const
	{
		return static_cast<double>(animation.get_frame_number());
	}
	double get_animation_length()const;

	bool start_animation(const Atom & name, bool no_restart = false);
	
	Atom get_animation_name()
	{
		return animation.get_animation_name();
	}

	/** Returns true if this animation will actually animate something on screen. 
	*	Returns false if this is an animation with single frame */
	bool needs_life_cycle() const;

	static const Atom node_name;
	static const Atom transparency_name;
	static const Atom color_name;
	static const Atom START_COMMAND_NAME;

	bool is_finished()const
	{
		return animation.is_finished();
	}
	
	int get_width() const
	{
		return (int) animation.get_width();
	}
	
	int get_height() const
	{
		return (int) animation.get_height();
	}
	
	virtual math::Rect<float> GetLocalBounds() LIBA_OVERRIDE
	{
		return math::Rect<float>(
			0.0f,
			0.0f,
			static_cast<float>(this->get_width()),
			static_cast<float>(this->get_height())
			);
	}

	/*math::Rect<int> get_rect() const
	{
		return math::Rect<int>(0, 0, (int) animation.get_width(), (int) animation.get_height());
	}*/

	enum AlignmentType { CENTER, LEFT, RIGHT, TOP, BOTTOM };
	static converter::EnumPair<char, AlignmentType> AlignmentTypeNames[];
	AlignmentType x_align;
	AlignmentType y_align;

	virtual Node* Clone() LIBA_OVERRIDE;
	void disable_loop();

	virtual void CreateProxy(ProxyVisitor * proxyVisitor) LIBA_OVERRIDE;

	void ForceUpdate()
	{
		need_update = true;
	}

	size_t GetSequencesCount()
	{
		return animation.GetSequencesCount();
	}

	anim2d::AbstractSequence * GetSequence(size_t index)
	{
		return animation.GetSequence(index);
	}

	std::string GetStartSequence() const
	{
		return animation.GetStartSequence();
	}

	void SetStartSequence(const std::string & value)
	{
		animation.SetStartSequence(value);
	}

	std::list< anim2d::AbstractSequence* >::iterator SequencesBegin()
	{
		return animation.SequencesBegin();
	}

	std::list< anim2d::AbstractSequence* >::iterator SequencesEnd()
	{
		return animation.SequencesEnd();
	}

	void AddSequence(anim2d::AbstractSequence * sequence)
	{
		animation.AddSequence(sequence);
	}

	void RemoveSequence(anim2d::AbstractSequence * sequence)
	{
		animation.RemoveSequence(sequence);
	}
protected:
	virtual bool on_node( xml::Provider * prov, const std::string & name ) LIBA_OVERRIDE;
	virtual bool on_attribute( xml::Provider * prov, const std::string & name, const std::string & value ) LIBA_OVERRIDE;
	virtual bool on_end_node( xml::Provider * prov ) LIBA_OVERRIDE;
	virtual void on_add_hardware(hard::Manager::hardware_type * ha) LIBA_OVERRIDE;
	virtual void on_save_attributes(xml::SaverBase * saver) const LIBA_OVERRIDE;
	virtual void on_save_nodes(xml::SaverBase * saver) const LIBA_OVERRIDE;
private:
	anim2d::AnimatedSequences animation;
	properties::DependencyProperty<double> transparency;
	properties::DependencyProperty<Color> color;
	bool need_update;

	void fill_vb();

	void SetFramePercent(double value);
	properties::VirtualDependencyProperty<double> framePercent;
	void SetFrameNumber(double value);
	properties::VirtualDependencyProperty<double> frameNumber;

	void StartCommandInvoked(liba::Atom arg);
	properties::Command<liba::Atom> startCommand;
};

} // namespace node
} // namespace lib3d

namespace converter
{

	template<>
	struct ConverterTraits<char, lib3d::node::Animation2D::AlignmentType>
		:	public ConverterTraitsEnumBase<char, lib3d::node::Animation2D::AlignmentType, lib3d::node::Animation2D::AlignmentTypeNames>
	{};

} // namespace converter

} // namespace liba
using namespace liba;